require(
    [
        "jquery", "framework/graphic/WebGL",
        "framework/asset/AssetSyst",
        "framework/Util",
        "render/pick/PickRenderSyst",
        "render/static/StaticRenderSyst",
        "render/basic/BasicRenderSyst",
        "render/basic/BasicRenderBatch",
        "render/sprite/SpriteBatch",
        "render/BufferMaker",
        "osm/OsmInfoSyst",
        "ui/QueryViewSyst",
        "ui/Compass",
        "framework/cam/Cam",
        "entity/Tile",
        "framework/FPSLogger",
        "framework/Timer",
        "framework/cam/behavior/CamOrbit",
        "framework/input/Input",
        "SystemManager"
    ],
    function(
        $, WebGL,
        AssetSyst,
        fwUtil,
        PickRenderSyst,
        StaticRenderSyst,
        BasicRenderSyst,
        BasicRenderBatch,
        SpriteBatch,
        BufferMaker,
        OsmInfoSyst,
        QueryViewSyst,
        Compass,
        Cam,
        Tile,
        FPSLogger,
        Timer,
        CamOrbit,
        Input,
        SystemManager
    ) {
        fwUtil.boilerPlate();

        //Create things
        var timer = Timer.create();
        var logger = FPSLogger.create();
        Input.init($('canvas'), timer);

        //Create cam
        var cam = Cam.create();
        cam.pos = vec3.create([0, 100, 0]);
        cam.target = vec3.create([0, 0, 0]);
        cam.aspect = WebGL.getAspectRatio();
        cam.far = 3000;
        cam.update();

        //Create camera controller
        var camController = CamOrbit.create(cam);

        //Initialize systems
        var systemMgr = SystemManager.create();
        var sRender = StaticRenderSyst.create(systemMgr, "vStatic|fStatic|shader.sp");
        var bRender = BasicRenderSyst.create(systemMgr, "vBasic|fBasic|shader.sp");
        var pRender = PickRenderSyst.create(systemMgr, "vPick|fPick|shader.sp");
        var sprRender = SpriteBatch.create(systemMgr, "vSprite|fSprite|shader.sp");
        OsmInfoSyst.create(systemMgr);
        QueryViewSyst.create(systemMgr, cam);
        systemMgr.initializeAll();

        //Create compas
        var compas = Compass.create().init(systemMgr, "compass.png", WebGL.viewportWidth/-2 + 64, WebGL.viewportHeight/2 - 64);

        //Get url params
        var params = (function getUrlVars() {
            var vars    = [];
            var curUrl  = window.location.href;
            var params  = curUrl.slice(curUrl.indexOf('?') + 1).split('&');
            params.forEach(function(val){
                var p = val.split('=');
                vars[p[0]] = p[1];
            });
            return vars;
        })();


        //Create tile
        var src = params['src'] || "osm/map.osm";
        var tile = Tile.create().init(systemMgr, src, function(tile) {
            vec3.set(tile.getCenterPos(), camController.target);
            camController.distance = 200;
            cam.update();
        });

        //Main loop
        function run() {
            //Get delta time since last frame
            timer.update();
            var delta = timer.getLastDelta();

            //Update things
            logger.update(delta);
            compas.update(cam);

            //Make sure cam is inside bound
            var bound = tile.getBounds();
            if(bound) {
                if(bound[0] > camController.target[0]) 
                    camController.target[0] = bound[0];
                else if(bound[0] + bound[2] < camController.target[0])
                    camController.target[0] = bound[0] + bound[2];
                
                if(bound[1] > camController.target[2]) 
                    camController.target[2] = bound[1];
                else if(bound[1] + bound[3] < camController.target[2])
                    camController.target[2] = bound[1] + bound[3];
            }
            camController.update(delta);

            //Check for picking
            if(Input.wasClicked()) {
                pRender.pick(
                    cam,
                    Input.pos[0], WebGL.viewportHeight - Input.pos[1]
                );
            }

            //Clear screen
            WebGL.gl.clearColor(0.53, 0.53, 0.53, 1.0);
            WebGL.gl.clear(WebGL.gl.COLOR_BUFFER_BIT | WebGL.gl.DEPTH_BUFFER_BIT);

            //Draw
            sRender.render(cam);
            bRender.render(cam);

            //Draw UIs
            WebGL.gl.disable(WebGL.gl.DEPTH_TEST);
            sprRender.render();
            WebGL.gl.enable(WebGL.gl.DEPTH_TEST);

            //Update input
            Input.update();
            
            //Request next frame
            requestAnimFrame(run);
        }

        run();
    }
);
